/*
 * Toedo
 * Copyright (C) 2009-2010 J. Devauchelle and contributors.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 3 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

package org.toedo.gui.views;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.util.Util;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.forms.events.ExpansionAdapter;
import org.eclipse.ui.forms.events.ExpansionEvent;
import org.eclipse.ui.forms.events.HyperlinkEvent;
import org.eclipse.ui.forms.events.IHyperlinkListener;
import org.eclipse.ui.forms.widgets.Form;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Hyperlink;
import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.ui.part.ViewPart;
import org.toedo.controllers.Controller;
import org.toedo.controllers.DataController;
import org.toedo.controllers.events.EventConstants;
import org.toedo.controllers.events.EventController;
import org.toedo.gui.actions.NewNoteAction;
import org.toedo.gui.actions.NewTaskAction;
import org.toedo.gui.actions.OpenTaskAction;
import org.toedo.gui.dialogs.TagsManagementDialog;
import org.toedo.gui.translations.Messages;
import org.toedo.model.Task;
import org.toedo.preferences.PreferenceConstants;
import org.toedo.Activator;

public class DashboardView extends ViewPart implements PropertyChangeListener {
	
	public static final String ID = "toedo-core.DashboardView"; //$NON-NLS-1$

	private Composite mParent;
	private FormToolkit mToolkit;
	private Form mForm;
	
	private Table mStartTodayTable;
	private Table mDueTodayTable;
	private Table mUrgentTable;
	private Table mLateTable;
	
	private TableViewer mStartTodayViewer;
	private TableViewer mDueTodayViewer;
	private TableViewer mUrgentViewer;
	private TableViewer mLateViewer;
	
	private Text mNewTaskTitle;
	private Button mNewTaskButton;
	
	private Text mNewNoteTitle;
	private Button mNewNoteButton;
	
	private Hyperlink mManageTags;
	
	private Image mStatusClosed;
	private Image mStatusOk;
	private Image mStatusLate;
	private Image mStatusUrgent;
	
	private Image mDashboardImage;
	
	private IAction mDefaultCutAction;
	private IAction mDefaultCopyAction;
	private IAction mDefaultPasteAction;
	
	private enum ContentFilterMode {
		EnStartToday,
		EnDueForToday,
		EnUrgent,
		EnLate
	}
	
	class TaskTableContentProvider implements IStructuredContentProvider {
		
		private ContentFilterMode mMode;
		
		public TaskTableContentProvider(ContentFilterMode mode) {
			mMode = mode;
		}
		
		public Object[] getElements(Object inputElement) {
			switch (mMode) {
			case EnStartToday : return DataController.getInstance().getProject().getStartingTodayTasksList().toArray();
			case EnDueForToday : return DataController.getInstance().getProject().getDueForTodayTasksList().toArray();
			case EnUrgent : return DataController.getInstance().getProject().getUrgentTasksList().toArray();
			case EnLate : return DataController.getInstance().getProject().getLateTasksList().toArray();
			default : return DataController.getInstance().getProject().getTasksList().toArray();
			}
		}
		public void dispose() {
		}
		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
		}
	}
	
	class TaskTableLabelProvider extends LabelProvider implements ITableLabelProvider {
		public Image getColumnImage(Object element, int columnIndex) {
			if (columnIndex == 0) {
				Task task = (Task) element;
				switch (task.getStatus()) {
				case EnOpen : if (task.isLate()) {
					return mStatusLate;
				} if (task.isUrgent()) {
					return mStatusUrgent;
				} else {
					return mStatusOk;
				}
				case EnClosed : return mStatusClosed;
				default : return mStatusOk;
				}
			} else {
				return null;
			}
		}
		public String getColumnText(Object element, int columnIndex) {
			switch(columnIndex) {
			case 1 : return ((Task) element).getTitle();
			default : return ""; //$NON-NLS-1$
			}
		}
	}
	
	public DashboardView() {
		Activator.getDefault();
		mStatusOk = Activator.getImageDescriptor("/icons/status-ok-16.png").createImage(); //$NON-NLS-1$
		mStatusClosed = Activator.getImageDescriptor("/icons/status-closed-16.png").createImage(); //$NON-NLS-1$
		mStatusLate = Activator.getImageDescriptor("/icons/status-late-16.png").createImage(); //$NON-NLS-1$
		mStatusUrgent = Activator.getImageDescriptor("/icons/status-urgent-16.png").createImage(); //$NON-NLS-1$
		
		mDashboardImage = Activator.getImageDescriptor("/icons/dashboard-16.png").createImage(); //$NON-NLS-1$
	}

	@Override
	public void createPartControl(Composite parent) {
		mParent = parent;
		
		setPartName(Messages.DashboardView_Title);
		
		mToolkit = new FormToolkit(mParent.getDisplay());
		mForm = mToolkit.createForm(mParent);
		mForm.setText(getPartName());
		mForm.setImage(mDashboardImage);
		
		mToolkit.decorateFormHeading(mForm);
		
		mForm.getBody().setLayout(new GridLayout(2, true));		
		
		// Tools section
		int toolsSectionStyle = Section.TITLE_BAR | Section.TWISTIE;
		if (Controller.getInstance().getPreferenceStore().getBoolean(PreferenceConstants.DASHBOARD_TOOLS_EXPANDED)) {
			toolsSectionStyle = toolsSectionStyle | Section.EXPANDED;
		}
		final Section toolsSection = mToolkit.createSection(mForm.getBody(), toolsSectionStyle);
		toolsSection.setText(Messages.DashboardView_ToolsSection);
		toolsSection.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
		
		Composite toolsComposite = mToolkit.createComposite(toolsSection);
		toolsComposite.setLayout(new GridLayout(3, false));
		
		mToolkit.createLabel(toolsComposite, Messages.DashboardView_NewTaskLabel);
		
		mNewTaskTitle = mToolkit.createText(toolsComposite, ""); //$NON-NLS-1$
		mNewTaskTitle.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
		mNewTaskTitle.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetDefaultSelected(SelectionEvent e) {
				processCreateTask(mNewTaskTitle);
			}			
		});
		mNewTaskTitle.addFocusListener(new FocusListener() {			
			@Override
			public void focusLost(FocusEvent e) {
				unregisterStandardActions();
			}			
			@Override
			public void focusGained(FocusEvent e) {
				registerStandardActions(mNewTaskTitle);		
			}
		});
		
		mNewTaskButton = mToolkit.createButton(toolsComposite, Messages.DashboardView_CreateNewTaskButton, SWT.NONE);
		mNewTaskButton.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				processCreateTask(mNewTaskTitle);
			}			
		});
		
		mToolkit.createLabel(toolsComposite, Messages.DashboardView_NewNote);
		
		mNewNoteTitle = mToolkit.createText(toolsComposite, ""); //$NON-NLS-1$
		mNewNoteTitle.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
		mNewNoteTitle.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetDefaultSelected(SelectionEvent e) {
				processCreateNote(mNewNoteTitle);
			}			
		});
		mNewNoteTitle.addFocusListener(new FocusListener() {			
			@Override
			public void focusLost(FocusEvent e) {
				unregisterStandardActions();
			}			
			@Override
			public void focusGained(FocusEvent e) {
				registerStandardActions(mNewNoteTitle);		
			}
		});
		
		mNewNoteButton = mToolkit.createButton(toolsComposite, Messages.DashboardView_CreateNewTaskButton, SWT.NONE);
		mNewNoteButton.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				processCreateNote(mNewNoteTitle);
			}			
		});
		
		mManageTags = mToolkit.createHyperlink(toolsComposite, Messages.DashboardView_ManageTagsLabel, SWT.NONE);
		mManageTags.addHyperlinkListener(new IHyperlinkListener(){
			@Override
			public void linkActivated(HyperlinkEvent e) {
				new TagsManagementDialog(mParent.getShell()).open();
			}
			@Override
			public void linkEntered(HyperlinkEvent e) {	}
			@Override
			public void linkExited(HyperlinkEvent e) { }
		});
		
		mToolkit.paintBordersFor(toolsComposite);
		toolsSection.setClient(toolsComposite);
		
		toolsSection.addExpansionListener(new ExpansionAdapter() {
			public void expansionStateChanged(ExpansionEvent e) {
				Controller.getInstance().getPreferenceStore().setValue(PreferenceConstants.DASHBOARD_TOOLS_EXPANDED, toolsSection.isExpanded());		
			}
		});
		
		// Start Today section
		Section startTodaySection = mToolkit.createSection(mForm.getBody(), 
				Section.TITLE_BAR | Section.EXPANDED);
		startTodaySection.setText(Messages.DashboardView_TasksStartTodaySection);
		startTodaySection.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
		
		Composite startTodayComposite = mToolkit.createComposite(startTodaySection);
		startTodayComposite.setLayout(new GridLayout(1, false));
		
		mStartTodayTable = mToolkit.createTable(startTodayComposite, SWT.FULL_SELECTION);
		mStartTodayTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));		
				
		mToolkit.paintBordersFor(startTodayComposite);
		startTodaySection.setClient(startTodayComposite);
		
		mStartTodayViewer = new TableViewer(mStartTodayTable);
		
		final TableColumn startTodayColumnIcon = new TableColumn(mStartTodayTable, SWT.NONE);
		startTodayColumnIcon.setText("");		 //$NON-NLS-1$
		final TableColumn startTodayColumnTitle = new TableColumn(mStartTodayTable, SWT.NONE);
		startTodayColumnTitle.setText(Messages.DashboardView_TitleColumn);
		
		mStartTodayViewer.setContentProvider(new TaskTableContentProvider(ContentFilterMode.EnStartToday));
		mStartTodayViewer.setLabelProvider(new TaskTableLabelProvider());
		mStartTodayViewer.setInput(getViewSite());
		
		// Due today section.
		Section dueForTodaySection = mToolkit.createSection(mForm.getBody(), 
				Section.TITLE_BAR | Section.EXPANDED);
		dueForTodaySection.setText(Messages.DashboardView_TasksDueTodaySection);
		dueForTodaySection.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
		
		Composite dueForTodayComposite = mToolkit.createComposite(dueForTodaySection);
		dueForTodayComposite.setLayout(new GridLayout(1, false));
		
		mDueTodayTable = mToolkit.createTable(dueForTodayComposite, SWT.FULL_SELECTION);
		mDueTodayTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
		
		mToolkit.paintBordersFor(dueForTodayComposite);
		
		dueForTodaySection.setClient(dueForTodayComposite);
		mDueTodayViewer = new TableViewer(mDueTodayTable);
		
		final TableColumn dueTodayColumnIcon = new TableColumn(mDueTodayTable, SWT.NONE);
		dueTodayColumnIcon.setText("");		 //$NON-NLS-1$
		final TableColumn dueTodayColumnTitle = new TableColumn(mDueTodayTable, SWT.NONE);
		dueTodayColumnTitle.setText(Messages.DashboardView_TitleColumn);
		
		mDueTodayViewer.setContentProvider(new TaskTableContentProvider(ContentFilterMode.EnDueForToday));
		mDueTodayViewer.setLabelProvider(new TaskTableLabelProvider());
		mDueTodayViewer.setInput(getViewSite());								
		
		// Urgent section
		Section urgentSection = mToolkit.createSection(mForm.getBody(), 
				Section.TITLE_BAR | Section.EXPANDED);
		urgentSection.setText(Messages.DashboardView_UrgentTasksSection);
		urgentSection.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
		
		Composite urgentComposite = mToolkit.createComposite(urgentSection);
		urgentComposite.setLayout(new GridLayout(1, false));
		
		mUrgentTable = mToolkit.createTable(urgentComposite, SWT.FULL_SELECTION);
		mUrgentTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

		mToolkit.paintBordersFor(urgentComposite);
		
		urgentSection.setClient(urgentComposite);
		mUrgentViewer = new TableViewer(mUrgentTable);
		
		final TableColumn urgentColumnIcon = new TableColumn(mUrgentTable, SWT.NONE);
		urgentColumnIcon.setText("");		 //$NON-NLS-1$
		final TableColumn urgentColumnTitle = new TableColumn(mUrgentTable, SWT.NONE);
		urgentColumnTitle.setText(Messages.DashboardView_TitleColumn);
		
		mUrgentViewer.setContentProvider(new TaskTableContentProvider(ContentFilterMode.EnUrgent));
		mUrgentViewer.setLabelProvider(new TaskTableLabelProvider());
		mUrgentViewer.setInput(getViewSite());
		
		// Late section
		Section lateSection = mToolkit.createSection(mForm.getBody(), 
				Section.TITLE_BAR | Section.EXPANDED);
		lateSection.setText(Messages.DashboardView_LateTasksSection);
		lateSection.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
		
		Composite lateComposite = mToolkit.createComposite(lateSection);
		lateComposite.setLayout(new GridLayout(1, false));
		
		mLateTable = mToolkit.createTable(lateComposite, SWT.FULL_SELECTION);
		mLateTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
		
		mToolkit.paintBordersFor(lateComposite);
		
		lateSection.setClient(lateComposite);
		mLateViewer = new TableViewer(mLateTable);
		
		final TableColumn lateColumnIcon = new TableColumn(mLateTable, SWT.NONE);
		lateColumnIcon.setText("");		 //$NON-NLS-1$
		final TableColumn lateColumnTitle = new TableColumn(mLateTable, SWT.NONE);
		lateColumnTitle.setText(Messages.DashboardView_TitleColumn);
		
		mLateViewer.setContentProvider(new TaskTableContentProvider(ContentFilterMode.EnLate));
		mLateViewer.setLabelProvider(new TaskTableLabelProvider());
		mLateViewer.setInput(getViewSite());	
		
		// Lines visible is ugly under Windows.
		if (!Util.isWindows()) {
			mStartTodayTable.setLinesVisible(true);
			mDueTodayTable.setLinesVisible(true);
			mUrgentTable.setLinesVisible(true);
			mLateTable.setLinesVisible(true);
		}						

		mParent.pack();
		
		mStartTodayTable.addControlListener(new ControlAdapter() {
			public void controlResized(ControlEvent e) {
				int width = mStartTodayTable.getClientArea().width - mStartTodayTable.getVerticalBar().getSize().x;				
				startTodayColumnIcon.setWidth(20);
				startTodayColumnTitle.setWidth(width - 20);
			}
		});
		
		mDueTodayTable.addControlListener(new ControlAdapter() {
			public void controlResized(ControlEvent e) {
				int width = mDueTodayTable.getClientArea().width - mDueTodayTable.getVerticalBar().getSize().x;				
				dueTodayColumnIcon.setWidth(20);
				dueTodayColumnTitle.setWidth(width - 20);
			}
		});
		
		mUrgentTable.addControlListener(new ControlAdapter() {
			public void controlResized(ControlEvent e) {
				int width = mUrgentTable.getClientArea().width - mUrgentTable.getVerticalBar().getSize().x;				
				urgentColumnIcon.setWidth(20);
				urgentColumnTitle.setWidth(width - 20);
			}
		});
		
		mLateTable.addControlListener(new ControlAdapter() {
			public void controlResized(ControlEvent e) {
				int width = mLateTable.getClientArea().width - mLateTable.getVerticalBar().getSize().x;				
				lateColumnIcon.setWidth(20);
				lateColumnTitle.setWidth(width - 20);
			}
		});
		
		mStartTodayViewer.addDoubleClickListener(new IDoubleClickListener() {
			public void doubleClick(DoubleClickEvent event) {
				processDoubleClick(mStartTodayViewer);				
			}
		});
		
		mDueTodayViewer.addDoubleClickListener(new IDoubleClickListener() {
			public void doubleClick(DoubleClickEvent event) {
				processDoubleClick(mDueTodayViewer);				
			}
		});
		
		mUrgentViewer.addDoubleClickListener(new IDoubleClickListener() {
			public void doubleClick(DoubleClickEvent event) {
				processDoubleClick(mUrgentViewer);				
			}
		});
		
		mLateViewer.addDoubleClickListener(new IDoubleClickListener() {
			public void doubleClick(DoubleClickEvent event) {
				processDoubleClick(mLateViewer);				
			}
		});
		
		EventController.getInstance().addGeneralEventsListener(this);
	}
	
	private void processDoubleClick(TableViewer viewer) {
		ISelection selection = viewer.getSelection();
		Object obj = ((IStructuredSelection)selection).getFirstElement();
		if (obj != null) {
			new OpenTaskAction((Task) obj).run();
		}
	}
	
	private void processCreateTask(Text text) {
		String taskName = text.getText();
		if ((taskName != null) &&
				(!taskName.isEmpty())) {
			new NewTaskAction(mParent.getShell(), taskName, false).run();
			text.setText(""); //$NON-NLS-1$
		}
	}
	
	private void processCreateNote(Text text) {
		String noteName = text.getText();
		if ((noteName != null) &&
				(!noteName.isEmpty())) {
			new NewNoteAction(mParent.getShell(), noteName, false).run();
			text.setText(""); //$NON-NLS-1$
		}
	}
	
	private void registerStandardActions(final Text text) {
		mDefaultCutAction = new Action() {
			public void run() {
				text.cut();
			}
		};
		
		mDefaultCopyAction = new Action() {
			public void run() {
				text.copy();
			}
		};
		
		mDefaultPasteAction = new Action() {
			public void run() {
				text.paste();
			}
		};
		
		IActionBars actionBars = getViewSite().getActionBars();
		actionBars.clearGlobalActionHandlers();
		actionBars.setGlobalActionHandler(ActionFactory.CUT.getId(), mDefaultCutAction);
		actionBars.setGlobalActionHandler(ActionFactory.COPY.getId(), mDefaultCopyAction);
		actionBars.setGlobalActionHandler(ActionFactory.PASTE.getId(), mDefaultPasteAction);
		actionBars.updateActionBars();
	}
	
	private void unregisterStandardActions() {
		IActionBars actionBars = getViewSite().getActionBars();
		actionBars.clearGlobalActionHandlers();
		actionBars.updateActionBars();
	}

	@Override
	public void setFocus() {
		// TODO Auto-generated method stub

	}

	@Override
	public void propertyChange(PropertyChangeEvent arg0) {
		class FullRefresher implements Runnable {					
			public void run() {											
				mStartTodayViewer.refresh();
				mDueTodayViewer.refresh();
				mUrgentViewer.refresh();
				mLateViewer.refresh();
			}
		}
		
		if (arg0.getPropertyName().equals(EventConstants.EVT_OPEN)) {
			Display.getDefault().asyncExec(new FullRefresher());
		} else if ((arg0.getPropertyName().equals(EventConstants.EVT_NEW_TAG)) ||
				(arg0.getPropertyName().equals(EventConstants.EVT_DELETE_TAG)) ||
				(arg0.getPropertyName().equals(EventConstants.EVT_ADD_TASK)) ||
				(arg0.getPropertyName().equals(EventConstants.EVT_TASKSLIST_SORT_CHANGE)) ||
				(arg0.getPropertyName().equals(EventConstants.EVT_PROJECT_PROPERTIES_CHANGE)) ||
				(arg0.getPropertyName().equals(EventConstants.EVT_RENAME_BASENOTE)) ||
				(arg0.getPropertyName().equals(EventConstants.EVT_DELETE_TASK)) ||
				(arg0.getPropertyName().equals(EventConstants.EVT_BASENOTE_TAG_CHANGE)) ||
				(arg0.getPropertyName().equals(EventConstants.EVT_MODIFY_TASK))) {
			Display.getDefault().asyncExec(new FullRefresher());
		}
		
	}
	
	public void dispose() {
		EventController.getInstance().removeGeneralEventsListener(this);
				
		mStatusOk.dispose();
		mStatusClosed.dispose();
		mStatusLate.dispose();
		mStatusUrgent.dispose();
		
		mDashboardImage.dispose();
		
		super.dispose();
	}

}
